package com.jintian.smart.kernel.flowable.engine;

import com.jintian.smart.kernel.common.util.StringUtil;
import com.jintian.smart.kernel.core.flow.FlowAction;
import com.jintian.smart.kernel.core.flow.IActionInvoker;
import com.jintian.smart.kernel.core.flow.IFlowEventListener;
import com.jintian.smart.kernel.core.flow.IProcessContext;
import com.jintian.smart.kernel.flowable.context.DefaultProcessContext;
import com.jintian.smart.kernel.flowable.listener.FlowEventListenerManager;
import com.jintian.smart.kernel.module.Model;
import lombok.extern.slf4j.Slf4j;
import org.beetl.sql.core.SQLManager;
import org.flowable.bpmn.model.ValuedDataObject;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.engine.ProcessEngine;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.TaskInfo;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import java.util.*;
import java.util.concurrent.Callable;

@Slf4j
public class DefaultActionInvoker implements IActionInvoker<TaskInfo>, ApplicationContextAware {
    private Map<IFlowEventListener<List<TaskInfo>>, Set<String>> listeners = new HashMap<>();

    private ProcessEngine engine;

    @Autowired
    private SQLManager sqlManager;

    @Autowired
    private FlowEventListenerManager flowEventListenerManager;

    protected ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    private ProcessEngine getProcessEngine() {
        if (this.engine == null) {
            this.engine = this.applicationContext.getBean(ProcessEngine.class);
        }
        return this.engine;
    }

    private void appendDefaultTransientVariables(DefaultProcessContext defaultProcessContext) throws ClassNotFoundException {
        if (!defaultProcessContext.getTransientVariables().containsKey(FlowAction.TRANSIENT_VARNAME_BIZBEAN)) {
            String bizClass = null;
            String modelName = null;
            if (StringUtil.isNotBlank(defaultProcessContext.getProcessInstanceId())) {
                // 有流程实例
                ProcessInstance pi = this.getProcessEngine().getRuntimeService().createProcessInstanceQuery()
                        .includeProcessVariables().processInstanceId(defaultProcessContext.getProcessInstanceId())
                        .singleResult();
                bizClass = (String) pi.getProcessVariables().get(FlowAction.VARNAME_START_BIZCLASS);
                modelName = (String) pi.getProcessVariables().get(FlowAction.VARNAME_START_MODELNAME);

            } else {
                // 无流程实例
                bizClass = (String) defaultProcessContext.getVariables().get(FlowAction.VARNAME_START_BIZCLASS);
                modelName = (String) defaultProcessContext.getVariables().get(FlowAction.VARNAME_START_MODELNAME);
                if (bizClass == null || modelName == null) {
                    ProcessDefinition pd = this.getProcessEngine().getRepositoryService().createProcessDefinitionQuery()
                            .processDefinitionKey(defaultProcessContext.getProcess()).latestVersion().singleResult();
                    if (pd == null) {
                        throw new IllegalArgumentException("流程" + defaultProcessContext.getProcess() + "尚未发布");
                    }
                    org.flowable.bpmn.model.Process process = this.getProcessEngine().getRepositoryService().getBpmnModel(pd.getId()).getMainProcess();
                    List<ValuedDataObject> dataObjects = process.getDataObjects();
                    for (ValuedDataObject obj : dataObjects) {
                        if (obj.getId().equals(FlowAction.VARNAME_START_BIZCLASS) && bizClass == null) {
                            bizClass = (String) obj.getValue();
                        } else if (obj.getId().equals(FlowAction.VARNAME_START_MODELNAME) && modelName == null) {
                            modelName = (String) obj.getValue();
                        }
                    }
                }
            }
            Class<?> bizBeanClass = null;
            Object bizBean = null;
            if (bizClass != null) {
                // if (modelName != null && this.applicationContext.getParent() != null) {
                // TODO 集成开发测试
                if (StringUtil.isNotBlank(modelName)) {
                    Model model = this.applicationContext.getBean("model$" + modelName, Model.class);
                    bizBeanClass = model.getModelContext().getClassLoader().loadClass(bizClass);
                } else {
                    bizBeanClass = this.applicationContext.getClassLoader().loadClass(bizClass);
                }
                bizBean = this.sqlManager.single(bizBeanClass, defaultProcessContext.getBusinessKey());
            }
            defaultProcessContext.getTransientVariables().put(FlowAction.TRANSIENT_VARNAME_BIZBEAN, bizBean);
        }

        if (!defaultProcessContext.getTransientVariables().containsKey(FlowAction.TRANSIENT_VARNAME_OPERATOR)) {
            defaultProcessContext.getTransientVariables().put(FlowAction.TRANSIENT_VARNAME_OPERATOR, defaultProcessContext.getOperator());
        }
    }

    @Override
    public List<TaskInfo> invoke(IProcessContext<List<TaskInfo>> context, Callable<List<TaskInfo>> callable) throws Exception {
        DefaultProcessContext defaultProcessContext = (DefaultProcessContext) context;
        this.appendDefaultTransientVariables(defaultProcessContext);
        try {
            flowEventListenerManager.triggerBeforeListener(context.getProcess(), context.getActivity(), context.getAction(), context);
            List<TaskInfo> result;
            try {
                result = callable.call();
            } catch (Exception e) {
                if (e instanceof RuntimeException) {
                    throw (RuntimeException) e;
                }
                throw new FlowableException(context.getAction().getTitle() + "错误," + e.getMessage(), e);
            }
            List<TaskInfo> newTasks = defaultProcessContext.getNewTasks();
            if (defaultProcessContext.getAction().equals(FlowAction.start) && newTasks.size() > 0) {
                defaultProcessContext.setProcessInstanceId(newTasks.get(0).getProcessInstanceId());
            }
            ProcessInstance pi = this.getProcessEngine().getRuntimeService().createProcessInstanceQuery().processInstanceId(context.getProcessInstanceId()).singleResult();
            defaultProcessContext.setProcessFinished(pi == null || pi.isEnded());
            ((DefaultProcessContext) context).setResult(result);

            flowEventListenerManager.triggerAfterListener(context.getProcess(), context.getActivity(), context.getAction(), context);

            if (StringUtil.isNotBlank(defaultProcessContext.getBusinessStatus())) {
                this.getProcessEngine().getRuntimeService().updateBusinessStatus(context.getProcessInstanceId(), defaultProcessContext.getBusinessStatus());
            }
            return result;
        } finally {
            flowEventListenerManager.unbindProcessContext(context.getProcess(), context.getActivity(), context.getAction());
        }
    }

    public static void main(String[] args) throws Exception {
        System.out.println("firstflow@abc_aaaa@start@after".matches("firstflow\\@([\\w-])+\\@([\\w-])+\\@([\\w-])+"));
        System.out.println("firstflow@abc-aaaa@start@after".matches("firstflow\\@(abc-aaaa)+\\@([\\w-])+\\@([\\w-])+"));
    }
}
